package cloud.lysky.game.gamesampe;

import cloud.lysky.game.gamesampe.component.HealthComponent;
import cloud.lysky.game.gamesampe.component.SkillComponent;
import cloud.lysky.game.gamesampe.component.StatsComponent;
import cloud.lysky.game.gamesampe.constant.Phase;
import cloud.lysky.game.gamesampe.constant.Turn;
import cloud.lysky.game.gamesampe.entity.Enemy;
import cloud.lysky.game.gamesampe.entity.Player;
import cloud.lysky.game.gamesampe.entity.Skill;
import cloud.lysky.game.gamesampe.model.DamageResult;
import cloud.lysky.game.gamesampe.util.EnemyUtil;
import cloud.lysky.game.gamesampe.util.GameUtil;
import cloud.lysky.game.gamesampe.util.PlayerUtil;
import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.app.GameSettings;
import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.entity.Entity;
import javafx.animation.*;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.PickResult;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.almasb.fxgl.dsl.FXGL.*;

public class HelloFXGLApplication extends GameApplication {
    private static void onFX(Runnable r) { Platform.runLater(r); }
    private Phase phase = Phase.SELECT;   // 初始为“选择敌人”阶段
    private Turn turn = Turn.PLAYER_TURN; // 玩家先开始
    // ==== 玩家 / 敌人 ====
    private Entity player;
    private Entity enemy;

    // ==== 选择面板 ====
    private Pane selectPane;
    private Pane skillPanel;
    private ListView<Enemy> enemyListView;
    private ListView<Skill> skillListView;
    private Map<String, Enemy> enemyMap = new LinkedHashMap<>();


    // === 战斗日志 ===
    private VBox playerPanel;
    private VBox enemyPanel;
    private final ObservableList<String> battleLogs = FXCollections.observableArrayList();
    private ListView<String> logView;

    @Override
    protected void initSettings(GameSettings settings) {
        settings.setWidth(1200);
        settings.setHeight(840);
        settings.setTitle("小小怪战斗");
        settings.setAppIcon("app_icon.png");
        settings.setVersion("v1.0.0 dev");
    }

    @Override
    protected void initGame() {
        // 背景
        var bg = new Rectangle(getAppWidth(), getAppHeight(), Color.web("#202734"));
        getGameScene().addUINode(bg);
        // 加载敌人信息
        List<Enemy> enemies = EnemyUtil.loadEnemies();
        enemyMap = enemies.stream().collect(Collectors.toMap(Enemy::getCode, Function.identity()));
        // 创建玩家
        player = PlayerUtil.loadPlayer();
        addPlayer((Player) player);
        initSkillPane();
        // 战斗HUD
        playerPanel = buildPlayerPanel(player);
        enemyPanel  = new VBox();
        addUINode(playerPanel, 20, 20);
        addUINode(enemyPanel, getAppWidth() - 360, 20);
        addUINode(skillPanel, 500,300);
        playerPanel.setVisible(false);
        enemyPanel.setVisible(false);
        skillPanel.setVisible(false);

        // 日志栏
        initLogView();
        logView.setVisible(false);
        // 敌人选择面板
        buildSelectPane();
        addUINode(selectPane, 0, 0);
        selectPane.setVisible(true);

        pushMsg("请选择要挑战的敌人，然后点击【开始战斗】");
    }

    @Override
    protected void initInput() {
        onKeyDown(javafx.scene.input.KeyCode.SPACE, () -> {
            if (phase != Phase.BATTLE || turn != Turn.PLAYER_TURN) return;
        });
    }

    // ====== 敌人选择界面 ======
    private void buildSelectPane() {
        Label title = new Label("选择要挑战的敌人");
        title.setTextFill(Color.WHITE);
        title.setStyle("-fx-font-size: 18px; -fx-font-weight: bold;");

        enemyListView = new ListView<>();
        enemyListView.getItems().addAll(enemyMap.values());
        enemyListView.getSelectionModel().selectFirst();
        enemyListView.setPrefWidth(360);
        enemyListView.setPrefHeight(240);
        enemyListView.setCellFactory(lv -> new ListCell<Enemy>() {
            @Override
            protected void updateItem(Enemy item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                } else {
                    setText(item.getName());
                }
            }
        });
        Label tip = new Label("提示：进入战斗后按空格进行攻击。胜负已分后会回到此界面。");
        tip.setTextFill(Color.web("#d0d0d0"));

        Button startBtn = new Button("开始战斗");
        startBtn.setOnAction(e -> {
            Enemy selectedEnmy= enemyListView.getSelectionModel().getSelectedItem();
            if (selectedEnmy == null)
                return;
            enemy = selectedEnmy;
            startBattleWith(selectedEnmy);
        });


        VBox box = new VBox(12, title, enemyListView, startBtn, tip);
        box.setAlignment(Pos.CENTER_LEFT);
        box.setPadding(new Insets(20));
        box.setPrefWidth(420);
        box.setStyle("-fx-background-color: rgba(255,255,255,0.06); -fx-background-radius: 12;");

        Label help = new Label("""
                操作说明：
                1) 左侧选择一个敌人并开始战斗
                2) 战斗中按 [Space] 进行攻击
                3) 胜负已分后将回到此界面，可继续选择
                """);
        help.setTextFill(Color.WHITE);
        help.setStyle("-fx-line-spacing: 6px;");

        HBox root = new HBox(20, box, help);
        root.setPadding(new Insets(30));
        root.setAlignment(Pos.TOP_LEFT);

        selectPane = new StackPane(root);
        selectPane.setPrefSize(getAppWidth(), getAppHeight());
        selectPane.setStyle("-fx-background-color: transparent;");
    }

    private void initSkillPane() {
        List<Skill> skills = player.getComponent(SkillComponent.class).getSkills();
        skillListView = new ListView<>();
        skillListView.getItems().addAll(skills);
        skillListView.setPrefWidth(360);
        skillListView.setPrefHeight(240);
        skillListView.setCellFactory(lv -> new ListCell<Skill>() {
            @Override
            protected void updateItem(Skill item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                } else {
                    setText(item.getName());
                }
            }
        });
        skillListView.setOnMouseClicked(e -> {
                    Skill skill = skillListView.getSelectionModel().getSelectedItem();
                    if (skill == null) return; // 点在空白处
                    playerAttack(skill);
                    skillPanel.setVisible(false);
        });
        VBox box = new VBox(12, skillListView);
        box.setAlignment(Pos.CENTER_LEFT);
        box.setPadding(new Insets(20));
        box.setPrefWidth(420);
        box.setStyle("-fx-background-color: rgba(255,255,255,0.06); -fx-background-radius: 12;");
        HBox root = new HBox(20, box);
        root.setPadding(new Insets(30));
        root.setAlignment(Pos.TOP_LEFT);
        skillPanel = new StackPane(root);
        skillPanel.setStyle("-fx-background-color: transparent;");
    }


    // ====== 进入 / 退出 战斗 ======
    private void startBattleWith(Enemy def) {
        phase = Phase.BATTLE;
        battleLogs.clear();
        pushMsg("战斗开始！敌人：" + def.getName());

        // 恢复玩家满血
        var hp = player.getComponent(HealthComponent.class);
        hp.setToMax();

        // 创建敌人（每次新建）
        if (enemy != null) {
            enemy.removeFromWorld();
        }
        addEnemy(def);

        // 刷新敌方面板
        enemyPanel.getChildren().setAll(buildEnemyPanel(def));

        // 显示战斗 HUD / 日志，隐藏选择
        playerPanel.setVisible(true);
        enemyPanel.setVisible(true);
        logView.setVisible(true);
        selectPane.setVisible(false);

        // 显示技能选择面板
        skillPanel.setVisible(true);

        turn = Turn.PLAYER_TURN;
        pushMsg("玩家回合：按 Space 进行攻击");
    }

    private void backToSelect() {
        phase = Phase.SELECT;
        selectPane.setVisible(true);
        playerPanel.setVisible(false);
        enemyPanel.setVisible(false);
        logView.setVisible(false);
        pushMsg("请选择下一位要挑战的敌人");
    }

    // ====== 角色/面板/血条 ======
    private void addPlayer(Player player) {
        Color color = Color.web("#FFFFFF");
        Rectangle body = new Rectangle(120, 120, color.deriveColor(0,1,1,0.85));
        body.setArcWidth(18);
        body.setArcHeight(18);
        player.setPosition(120, 380);
        player.getViewComponent().addChild(body);
        FXGL.getGameWorld().addEntity(player);
    }

    private void addEnemy(Enemy enemy) {
        Color color = Color.web("#FFFFFF");
        Rectangle body = new Rectangle(120, 120, color.deriveColor(0,1,1,0.85));
        body.setArcWidth(18);
        body.setArcHeight(18);
        enemy.setPosition(640, 220);
        enemy.getViewComponent().addChild(body);
        FXGL.getGameWorld().addEntity(enemy);
    }

    private VBox buildPlayerPanel(Entity target) {
        var stats = target.getComponent(StatsComponent.class);
        Label name = whiteLabel();
        name.textProperty().bind(Bindings.concat("角色：", stats.nameProperty()));
        Label atk  = whiteLabel();
        atk.textProperty().bind(Bindings.concat("攻击力：", stats.atkProperty().asString()));
        Label def  = whiteLabel();
        def.textProperty().bind(Bindings.concat("防御力：", stats.defProperty().asString()));
        Label criticalHitRate = whiteLabel();
        criticalHitRate.textProperty().bind(Bindings.concat("暴击率：", stats.getCriticalHitRateStr()));
        Label criticalBones = whiteLabel();
        criticalBones.textProperty().bind(Bindings.concat("暴击效果：", stats.getCriticalBonusStr()));
        VBox hpBox = makeHPBoxWithLag(target, true);
        VBox panel = new VBox(6, name, atk, def,criticalHitRate,criticalBones, hpBox);
        panel.setPadding(new Insets(16));
        panel.setStyle("-fx-background-color: rgba(255,255,255,0.06); -fx-background-radius: 12;");
        return panel;
    }

    private VBox buildEnemyPanel(Entity enemy) {
        VBox hpBox = makeHPBoxWithLag(enemy, false);
        VBox panel = new VBox(6, hpBox);
        panel.setPadding(new Insets(16));
        panel.setStyle("-fx-background-color: rgba(255,255,255,0.06); -fx-background-radius: 12;");
        return panel;
    }

    private Label whiteLabel() {
        Label l = new Label();
        l.setTextFill(Color.WHITE);
        return l;
    }

    /** 具有“延迟掉血 + 半透明受击尾巴”的血条（矩形实现，真正透明可穿透） */
    private VBox makeHPBoxWithLag(Entity target, boolean showName) {
        var health = target.getComponent(HealthComponent.class);
        var stats  = target.getComponent(StatsComponent.class);
        Label title = new Label(showName ? stats.getName() : "敌方");
        title.setTextFill(Color.WHITE);
        final double W = 320, H = 22;
        DoubleProperty shownHP   = new SimpleDoubleProperty(health.getHp()); // 前景（即时）
        DoubleProperty delayedHP = new SimpleDoubleProperty(health.getHp()); // 背景（延迟追赶）

        // 可穿透的槽底色（半透明）
        Rectangle slot = new Rectangle(W, H);
        slot.setFill(Color.color(0,0,0,0.22));
        slot.setArcWidth(12); slot.setArcHeight(12);

        Rectangle back = new Rectangle(0, H);                 // 掉血尾巴（半透明红）
        back.setFill(Color.color(1, 0, 0, 0.25));
        back.setArcWidth(12); back.setArcHeight(12);

        Rectangle front = new Rectangle(0, H);
        front.setFill(Color.web("#cc0033"));
        front.setArcWidth(12); front.setArcHeight(12);

        StackPane hpStack = new StackPane(slot, back, front);
        StackPane.setAlignment(back, Pos.CENTER_LEFT);
        StackPane.setAlignment(front, Pos.CENTER_LEFT);
        hpStack.setMaxSize(W, H);
        hpStack.setStyle("-fx-background-color: transparent;");

        // 宽度绑定
        back.widthProperty().bind(delayedHP.divide(health.maxHPProperty()).multiply(W));
        front.widthProperty().bind(shownHP.divide(health.maxHPProperty()).multiply(W));

        // 文本
        Label text = new Label();
        text.setTextFill(Color.WHITE);
        text.textProperty().bind(Bindings.createStringBinding(
                () -> String.format("HP: %d / %d", (int)Math.round(shownHP.get()), health.getMaxHP()),
                shownHP, health.maxHPProperty()
        ));

        // 动画：前景更快，背景延迟追赶
        health.hpProperty().addListener((obs, oldV, newV) -> {
            double oldVal = oldV.doubleValue();
            double newVal = newV.doubleValue();
            Timeline tFront = new Timeline(
                    new KeyFrame(Duration.ZERO,        new KeyValue(shownHP, shownHP.get())),
                    new KeyFrame(Duration.millis(200), new KeyValue(shownHP, newVal))
            );
            tFront.play();
            if (newVal < oldVal) {
                Timeline tBack = new Timeline(
                        new KeyFrame(Duration.millis(150), new KeyValue(delayedHP, delayedHP.get())),
                        new KeyFrame(Duration.millis(750), new KeyValue(delayedHP, newVal))
                );
                tBack.play();
            } else {
                delayedHP.set(newVal); // 回血时立即跟上
            }
        });
        return new VBox(4, title, hpStack, text);
    }

    // ====== 战斗流程 ======
    private void playerAttack(Skill skill) {
        jabAnimation(player, +18);
        DamageResult damage = computeDamage(player, enemy, skill);
        damageEntity(enemy, damage);

        log("%s 使用【%s】%s，对 %s 造成 %d 伤害",
                nameOf(player), damage.getFrom(), damage.isCritical() ? "（暴击）" : "", nameOf(enemy), damage.getDamage());
        if (skill.getEffect() != null) {
            skill.getEffect().apply(player, enemy, damage);
        }
        if (isDead(enemy)) {
            endBattle(true);
            return;
        }

        turn = Turn.ENEMY_TURN;
        pushMsg("敌人回合…");
        runOnce(this::enemyAttack, Duration.seconds(0.7));
    }

    private void enemyAttack() {
        List<Skill> skills = enemy.getComponent(SkillComponent.class).getSkills();
        Collections.shuffle(skills);
        Skill skill = skills.getFirst();

        jabAnimation(enemy, -18);

        DamageResult damage = computeDamage(enemy, player, skill);
        damageEntity(player, damage);
        if (skill.getEffect() != null) {
            skill.getEffect().apply(player, enemy, damage);
        }

        log("%s 使用【%s】%s，对 %s 造成 %d 伤害",
                nameOf(enemy), damage.getFrom(), damage.isCritical() ? "（暴击）" : "", nameOf(player), damage.getDamage());

        if (isDead(player)) {
            endBattle(false);
            return;
        }

        turn = Turn.PLAYER_TURN;
        skillPanel.setVisible(true);
        pushMsg("玩家回合：请选择技能");
    }

    private DamageResult computeDamage(Entity attacker, Entity defender, Skill skill) {
        StatsComponent a = attacker.getComponent(StatsComponent.class);
        StatsComponent d = defender.getComponent(StatsComponent.class);
        int rand = random(-20, 31);
        int atk = a.getAtk();
        boolean critical = GameUtil.isEventHappen(a.getCriticalHitRate());
        if (critical) {
            atk = (int) (atk * a.getCriticalBonus());
        }
        if (skill.getEffect() != null) {
            atk += skill.getEffect().apply(attacker, defender, null);
        }

        double base = atk + FXGL.random(skill.getLowATK(), skill.getHighATK()) - d.getDefense() + rand;
        int damage = Math.max(1, (int)Math.round(base));
        return DamageResult.builder().from(skill.getName()).critical(critical).damage(damage).build();
    }

    // ====== 视觉 / 伤害 / 结果 ======
    private void jabAnimation(Entity e, double dx) {
        Platform.runLater(() -> {
            Node node = e.getViewComponent().getChildren().getFirst();
            TranslateTransition tt = new TranslateTransition(Duration.seconds(0.12), node);
            tt.setByX(dx);
            tt.setAutoReverse(true);
            tt.setCycleCount(2);
            tt.play();
        });
    }

    private void damageEntity(Entity target, DamageResult damage) {
        HealthComponent hp = target.getComponent(HealthComponent.class);
        hp.damage(damage.getDamage());
        // 受击闪烁 + 飘字
        Platform.runLater(() -> {
            Node node = target.getViewComponent().getChildren().getFirst();
            double orig = node.getOpacity();
            node.setOpacity(0.6);
            new Timeline(new KeyFrame(Duration.millis(120), new KeyValue(node.opacityProperty(), orig))).play();
        });
        showFloatingText("-" + damage.getDamage(), target.getX() + 48, target.getY() - 10, Color.ORANGE);
    }

    private void showFloatingText(String s, double x, double y, Color color) {
        Platform.runLater(() -> {
            Label lbl = new Label(s);
            lbl.setTextFill(color);
            lbl.setStyle("-fx-font-size: 22px; -fx-font-weight: bold;");
            addUINode(lbl, x, y);
            var tt = new TranslateTransition(Duration.seconds(0.8), lbl);
            tt.setByY(-30);
            tt.setOnFinished(e -> getGameScene().removeUINode(lbl));
            tt.play();
        });
    }

    private boolean isDead(Entity e) {
        return e.getComponent(HealthComponent.class).getHp() <= 0;
    }

    private void endBattle(boolean playerWin) {
        turn = Turn.END;
        String msg = playerWin ? "🎉 胜利！" : "💀 战败…";
        pushMsg(msg);
        getDialogService().showMessageBox(msg + "\n返回选择界面继续挑战吧！", () -> {
            // 移除敌人、回到选择界面
            if (enemy != null) { enemy.removeFromWorld(); enemy = null; }
            backToSelect();
        });
    }

    // ====== 日志 ======
    private void initLogView() {
        logView = new ListView<>(battleLogs);
        logView.setFocusTraversable(false);
        logView.setPrefWidth(getAppWidth() - 40);
        logView.setPrefHeight(180);
        logView.setStyle("""
                -fx-background-color: rgba(0,0,0,0.35);
                -fx-control-inner-background: rgba(0,0,0,0.0);
                -fx-text-fill: white;
                """);
        addUINode(logView, 20, getAppHeight() - 200);
    }



    private void log(String fmt, Object... args) {
        String line = String.format(fmt, args);
        Platform.runLater(() -> {
            if (battleLogs.size() > 200) battleLogs.remove(0, battleLogs.size() - 200);
            battleLogs.add(line);
            logView.scrollTo(battleLogs.size() - 1);
        });
    }

    private void pushMsg(String msg) {
        onFX(() -> getNotificationService().pushNotification(msg)) ;
        log(">> %s", msg);
    }

    private String nameOf(Entity e) { return e.getComponent(StatsComponent.class).getName(); }

    public static void main(String[] args) {
        launch(args);
    }
}
